﻿//=====================================================================================================
// LocalizationLibrary
// The Localization Library is a collection of reusable software components that provide 
// support for localization. This library enables you to localize WPF, Silverlight and WP7 applications.
// (http://localizationlibrary.codeplex.com)
//=====================================================================================================
// Author: Nicola Gallo (Website: www.nicola.gallo.name, Email: nicolagmt@hotmail.it) 
//=====================================================================================================

namespace SLS.ExClassLib.Loc.Threading
{
    //.NET References
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Text;
    using System.Threading;

    /// <summary>
    /// Implements a worker to process the application's <see cref="WorkerJob"/>.
    /// </summary>
    internal class LocalizationWorker
    {
        //Fields

        private BackgroundWorker _backgroundWorker = new BackgroundWorker();
        private readonly Dictionary<DynamicResourceToken, LocalizationJob> Jobs = new Dictionary<DynamicResourceToken, LocalizationJob>();
        internal readonly DynamicResourceTokenQueue TokensQueue = new DynamicResourceTokenQueue();
        public volatile bool ForceTermination = false;

        //Properties

        /// <summary>
        /// Gets true if the worker is working, false otherwise.
        /// </summary>
        public bool IsStarted { get; private set; }

        /// <summary>
        /// Called when the <see cref="LocalizationProvider"/> has completed the localization.
        /// </summary>
        public LocalizationCompletionCallback OnLocalization { get; set; }

        //Methods

        /// <summary>
        /// Start the worker.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// Thrown whether the worker has already been started.
        /// </exception>
        public void Start()
        {
            if (this.IsStarted)
            {
                throw new InvalidOperationException("The worker has already been started.");
            }
            this._backgroundWorker.DoWork += new DoWorkEventHandler(OnStart);
            this._backgroundWorker.RunWorkerAsync();
        }

        /// <summary>
        /// Occurs when worker starts to work.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnStart(object sender, DoWorkEventArgs e)
        {
            while (!this.ForceTermination)
            {
                DynamicResourceToken token = null;
                lock (this.TokensQueue)
                {
                    if (this.TokensQueue.Count > 0)
                    {
                        token = this.TokensQueue.Dequeue();
                    }
                }
                if ((token != null) && (token.IsValide))
                {
                    lock (this.Jobs)
                    {
                        if (!this.Jobs.ContainsKey(token))
                        {
                            LocalizationJob job = new LocalizationJob(token);
                            job.OnLocalization = (localizedToken) =>
                            {
                                if (localizedToken != null)
                                {
                                    bool validLocalizationToken = false;
                                    lock (this.Jobs)
                                    {
                                        validLocalizationToken = this.Jobs.ContainsKey(localizedToken);
                                        if (validLocalizationToken)
                                        {
                                            this.Jobs.Remove(localizedToken);
                                        }
                                    }
                                    if ((validLocalizationToken) && (localizedToken.IsValide) && 
                                        (localizedToken.IsLocalized) && (this.OnLocalization != null))
                                    {
                                        this.OnLocalization(localizedToken);
                                    }
                                }
                            };
                            this.Jobs.Add(token, job);
                            job.Start();
                        }
                    }
                }
                DynamicResourceToken[] dynResTokens = null;
                lock (this.Jobs)
                {
                    dynResTokens = this.Jobs.Keys.ToArray();
                }
                LocalizationManager.SetCurrentTokenLocalization(dynResTokens);
            }
        }

        /// <summary>
        /// Aborts the job that is working on the input <paramref name="token"/>.
        /// </summary>
        /// <param name="token">Token</param>
        /// <returns>True if exist a job and it is killed, False otherwise.</returns>
        public bool Abort(DynamicResourceToken token)
        {
            bool retVal = false;
            lock (this.Jobs)
            {
                if (this.Jobs.ContainsKey(token))
                {
                    this.Jobs[token].Abort();
                    this.Jobs.Remove(token);
                    retVal = true;
                }
            }
            return retVal;
        }

        /// <summary>
        /// Resets the <see cref="LocalizationWorker"/>.
        /// </summary>
        public void Reset()
        {
            lock (this.TokensQueue)
                lock (this.Jobs)
                {
                    this.TokensQueue.Clear();
                    foreach (var job in this.Jobs)
                    {
                        if (job.Value.IsStarted)
                        {
                            job.Value.Abort();
                        }
                    }
                    this.Jobs.Clear();
                }
        }
    }
}